#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>


#define NUM 1024
#define OPT_NUM 64

#define NONE_R   0//默认没有重定向
#define INPUT_R  1//输入重定向标记
#define OUTPUT_R 2
#define APPEND_R 3

int rtype = NONE_R;//默认没有重定向
char* rfile = NULL;//重定向文件名称

char lineCommand[NUM];
char *myargv[OPT_NUM]; //指针数组
int  lastCode = 0;
int  lastSig = 0;

#define jumpspace(start) do{  while(isspace(*start))  ++start; }while(0)//这里不带分号，因为这个宏我们可以直接拿来做if语句的判断条件
void commandCheak(char* ck)
{
    assert(ck!=NULL);
    char* start = ck;
    char* end = ck + strlen(ck);//这里不用在最后加1，假设有4个字符，下标4就是最后的\0
    while(start!=end)
    {
        if(*start == '>')
        {
            *start='\0';
            ++start;
            if(*start=='>')
            {
                rtype=APPEND_R;
                ++start;//追加重定向要再加一次
            }
            else
            {
                rtype=OUTPUT_R;
            }
            jumpspace(start);
            rfile=start;
            break;
        }
        else if(*start == '<')
        {
            *start='\0';
            ++start;
            jumpspace(start);
            rtype = INPUT_R;
            rfile = start;
            break;
        }
        else
        {
            ++start;
        }
    }
}
int main()
{
    while(1)
    {
        rtype=NONE_R;
        rfile=NULL;
        // 输出提示符
        printf("用户名@主机名 当前路径# ");
        fflush(stdout);

        // 获取用户输入, 输入的时候，输入\n
        char *s = fgets(lineCommand, sizeof(lineCommand)-1, stdin);
        assert(s != NULL);
        (void)s;
        // 清除最后一个\n , abcd\n
        lineCommand[strlen(lineCommand)-1] = 0; // ?
        //printf("test : %s\n", lineCommand);
        
        // "ls -a -l -i" -> "ls" "-a" "-l" "-i" -> 1->n
        commandCheak(lineCommand);//将一个字符串分隔为两个字符串
        // 字符串切割
        myargv[0] = strtok(lineCommand, " ");
        int i = 1;
        if(myargv[0] != NULL && strcmp(myargv[0], "ls") == 0)
        {
            myargv[i++] = (char*)"--color=auto";
        }

        // 如果没有子串了，strtok->NULL, myargv[end] = NULL
        while(myargv[i++] = strtok(NULL, " "));

        // 如果是cd命令，不需要创建子进程,让shell自己执行对应的命令，本质就是执行系统接口
        // 像这种不需要让我们的子进程来执行，而是让shell自己执行的命令 --- 内建/内置命令
        if(myargv[0] != NULL && strcmp(myargv[0], "cd") == 0)
        {
            if(myargv[1] != NULL) chdir(myargv[1]);
            continue;
        }
        if(myargv[0] != NULL && myargv[1] != NULL && strcmp(myargv[0], "echo") == 0)
        {
            if(strcmp(myargv[1], "$?") == 0)
            {
                printf("%d, %d\n", lastCode, lastSig);
            }
            else
            {
                printf("%s\n", myargv[1]);
            }
            continue;
        }
        // 测试是否成功, 条件编译
#ifdef DEBUG
        for(int i = 0 ; myargv[i]; i++)
        {
            printf("myargv[%d]: %s\n", i, myargv[i]);
        }
#endif
        // 内建命令 --> echo

        // 执行命令
        pid_t id = fork();
        assert(id != -1);

        if(id == 0)
        {
            switch(rtype)//进行重定向处理！
            {
                case NONE_R:
                    break;
                case INPUT_R:
                    {
                        int fd = open(rfile,O_RDONLY);
                        assert(fd!=-1);
                        dup2(fd,0);
                    }
                    break;
                case OUTPUT_R:
                case APPEND_R:
                    {
                        umask(0);
                        int flags = O_WRONLY | O_CREAT;
                        if(rtype==APPEND_R) flags |= O_APPEND;
                        else flags |= O_TRUNC;
                        int fd = open(rfile,flags,0666);
                        assert(fd!=-1);
                        dup2(fd,1);
                    }
                    break;
                default:
                    printf("Is bug!!!\n");
                    break;
            }
            execvp(myargv[0], myargv);
            exit(1);
        }
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        assert(ret > 0);
        (void)ret;
        lastCode = ((status>>8) & 0xFF);
        lastSig = (status & 0x7F);
    }
}
